/*
 * @Author: Snow
 * @Date: 2021-09-02 10:55:04
 * @Description: 
 */
export class MatrixUtil {
    /**
     * @Author: Snow
     * @description: 矩阵和向量相乘
     * @param {number} matrix
     * @param {number} vec4
     * @return {*}
     */
    public static multiplyMatrixAndPoint(matrix: number[], vec4: number[]): number[] {
        if (matrix.length % vec4.length != 0) {
            console.warn("维度不匹配");
            return;
        }
        let ret = [];
        let len = vec4.length;
        let c = matrix.length / vec4.length;
        for (let i = 0; i < c; i++) {
            let num: number = 0;
            for (let j = 0; j < len; j++) {
                const element = vec4[j];
                num += element * matrix[i * len + j];
            }
            ret.push(num);
        }
        return ret;
    }

    /**
     * @Author: Snow
     * @description:矩阵相乘 
     * @param {number} matrixA
     * @param {number} matrixB
     * @return {*}
     */
    public static multiplyMatrices(matrixA: number[], matrixB: number[]): number[] {
        let column0: number[] = [matrixB[0], matrixB[4], matrixB[8], matrixB[12]];
        let column1: number[] = [matrixB[1], matrixB[5], matrixB[9], matrixB[13]];
        let column2: number[] = [matrixB[2], matrixB[6], matrixB[10], matrixB[14]];
        let column3: number[] = [matrixB[3], matrixB[7], matrixB[11], matrixB[15]];
        let result0 = MatrixUtil.multiplyMatrixAndPoint(matrixA, column0);
        let result1 = MatrixUtil.multiplyMatrixAndPoint(matrixA, column1);
        let result2 = MatrixUtil.multiplyMatrixAndPoint(matrixA, column2);
        let result3 = MatrixUtil.multiplyMatrixAndPoint(matrixA, column3);

        return [
            result0[0], result1[0], result2[0], result3[0],
            result0[1], result1[1], result2[1], result3[1],
            result0[2], result1[2], result2[2], result3[2],
            result0[3], result1[3], result2[3], result3[3]
        ]
    }

    /**
     * @Author: Snow
     * @description:平移矩阵 
     * @param {number} x
     * @param {number} y
     * @param {number} z
     * @return {*}
     */
    public static moveMatrix(x: number, y: number, z: number): number[] {
        const translationMatrix = [
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            x, y, z, 1
        ];
        return translationMatrix;
    }

    /**
     * @Author: Snow
     * @description: 缩放矩阵
     * @param {number} w
     * @param {number} h
     * @param {number} d
     * @return {*}
     */
    public static scaleMatrix(w: number, h: number, d: number): number[] {
        const translationMatrix = [
            w, 0, 0, 0,
            0, h, 0, 0,
            0, 0, d, 0,
            0, 0, 0, 1
        ];
        return translationMatrix;
    }

    /**
     * @Author: Snow
     * @description:  按照Z轴旋转
     * @param {number} a
     * @return {*}
     */
    public static rotateZMatrix(a: number): number[] {
        // 绕Z轴旋转
        const rotateZMatrix = [
            Math.cos(a), -Math.sin(a), 0, 0,
            Math.sin(a), Math.cos(a), 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ];

        return rotateZMatrix;
    }

    /**
     * @Author: Snow
     * @description:  按照X轴旋转
     * @param {number} a
     * @return {*}
     */
    public static rotateXMatrix(a: number): number[] {
        // 绕Z轴旋转
        const rotateXMatrix = [
            1, 0, 0, 0,
            0, Math.cos(a), -Math.sin(a), 0,
            0, Math.sin(a), Math.cos(a), 0,
            0, 0, 0, 1
        ];

        return rotateXMatrix;
    }

    /**
     * @Author: Snow
     * @description: Y轴旋转
     * @param {number} a
     * @return {*}
     */
    public static rotateYMatrix(a: number): number[] {
        // 绕Z轴旋转
        const rotateYMatrix = [
            Math.cos(a), 0, Math.sin(a), 0,
            0, 1, 0, 0,
            -Math.sin(a), 0, Math.cos(a), 0,
            0, 0, 0, 1
        ];

        return rotateYMatrix;
    }

    /**
     * @Author: Snow
     * @description: 
     * @param {number} out 输出矩阵
     * @param {number} fovy 
     * 一个以弧度表示的角度，指示观看者一层可以看多少场景。
     * 数字越大，摄像机可见的越多。边缘的几何形状变得越来越失真，等同于广角镜。
     * 当视野更大时，物体通常会变小。当视野较小时，摄像机在场景中的看到的东西会越来越少。
     * 物体因透视而变形的程度要小得多，并且物体似乎更靠近相机。

     * @param {number} aspect
      场景的宽高比，等于其宽度除以其高度。
      在本示例中，就是窗口的宽度除以窗口的高度。
      此参数的引入最终解决了当画布调整大小和形状时模型的变形问题。
     * @param {number} near 近景距离
      一个正数，表示到屏幕的距离是垂直于地板的平面的距离，该距离比将所有内容都裁剪的距离更近。
      它在裁剪空间中映射为-1，并且不应设置为0。
     * @param {number} far 远景距离
      一个正数，表示与平面之间的距离，超出该距离将裁剪几何体。
      它在裁剪空间中映射为1.
      该值应保持合理的距离以接近几何图形的距离，以免在渲染时出现精度误差。
     * @return {*}
     */
    public static perspective(out: number[], fovy: number, aspect: number, near: number, far: number): number[] {
        var f = 1.0 / Math.tan(fovy / 2),
            nf = 1 / (near - far);
        out[0] = f / aspect;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
        out[4] = 0;
        out[5] = f;
        out[6] = 0;
        out[7] = 0;
        out[8] = 0;
        out[9] = 0;
        out[10] = (far + near) * nf;
        out[11] = -1;
        out[12] = 0;
        out[13] = 0;
        out[14] = (2 * far * near) * nf;
        out[15] = 0;
        return out;
    }


}